package br.jus.cnj;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.joda.time.DateTime;
import org.joda.time.Days;

import br.jus.cnj.enums.utils.TipoMascaraData;
import br.jus.cnj.enums.utils.TipoMascaraHora;

public class DateUtils {

	/**
	 * <p>
	 * Faz um parse de uma data(java.util.Date) para uma string com a máscara passada no segundo parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.passarDataParaString(null,null)        = null
	 * DateUtils.passarDataParaString(s,TipoMascaraData.DD_MM_AAAA)          = "31/01/2014"
	 * DateUtils.passarDataParaString(data,TipoMascaraData.DD_MM_AAAA_HH_MM)    = "31/01/2014 15:59"
	 * DateUtils.passarDataParaString(data,TipoMascaraData.DD_MM_AAAA_HH_MM_SS) = "31/01/2014 15:59:00"
	 * DateUtils.passarDataParaString(data,TipoMascaraData.DD_MM_AA)            = "31/01/14"
	 * DateUtils.passarDataParaString(data,TipoMascaraData.DD_MM_AA_HH_MM)      = "31/01/14 15:59"
	 * DateUtils.passarDataParaString(data,TipoMascaraData.DD_MM_AA_HH_MM_SS)   = "31/01/14 15:59:00"
	 * </pre>
	 * 
	 * @param data
	 *            Data a ser passada, pode ser null porém sempre retornará null.
	 * @param tipoMascaraData
	 *            Data a ser passada pela máscara br.jus.cnj.enums.utils.TipoMascaraData, pode ser null porém sempre retornará null.
	 * @return <code>a String da data no formato da máscara passada</code>
	 */
	public static String passarDataParaString(Date data, TipoMascaraData tipoMascaraData) {

		return passarDataParaString(data, tipoMascaraData.getMascara());

	}

	/**
	 * <p>
	 * Passa uma string para uma data(java.util.Date) de acordo máscara passada por parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.extrairDadosHoraDeData(null,null)        = null
	 * DateUtils.extrairDadosHoraDeData(stringData,TipoMascaraData.DD_MM_AAAA)         
	 * DateUtils.extrairDadosHoraDeData(stringData,TipoMascaraData.DD_MM_AAAA_HH_MM)   
	 * DateUtils.extrairDadosHoraDeData(stringData,TipoMascaraData.DD_MM_AAAA_HH_MM_SS)
	 * DateUtils.extrairDadosHoraDeData(stringData,TipoMascaraData.DD_MM_AA)           
	 * DateUtils.extrairDadosHoraDeData(stringData,TipoMascaraData.DD_MM_AA_HH_MM)     
	 * DateUtils.extrairDadosHoraDeData(stringData,TipoMascaraData.DD_MM_AA_HH_MM_SS)
	 * </pre>
	 * 
	 * @param value
	 *            Data a ser passada, pode ser null porém sempre retornará null.
	 * @param tipoMascaraHora
	 *            Data a ser passada pela máscara br.jus.cnj.enums.utils.TipoMascaraHora, pode ser null porém sempre retornará null.
	 * @return <code>a Data através da máscara passada</code>
	 */
	public static String extrairDadosHoraDeData(Date data, TipoMascaraHora tipoMascaraHora) {

		return passarDataParaString(data, tipoMascaraHora.getMascara());

	}

	/**
	 * <p>
	 * Passa uma string para uma data(java.util.Date) de acordo máscara passada por parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.extrairHoraMinutoDeData(null)
	 * DateUtils.extrairHoraMinutoDeData(data) = "23:59"
	 * </pre>
	 * 
	 * @param value
	 *            Data a ser passada, pode ser null porém sempre retornará null.
	 * @return <code>a hora no padrão "HH:mm"</code>
	 */
	public static String extrairHoraMinutoDeData(Date data) {

		return extrairDadosHoraDeData(data, TipoMascaraHora.HH_MM);

	}

	/**
	 * <p>
	 * Método private utilizado por outros métodos públicos para a formatação de data
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.passarDataParaString(null,null)        = null
	 * DateUtils.passarDataParaString(stringData,TipoMascaraData.DD_MM_AAAA.getMascara())
	 * </pre>
	 * 
	 * @param value
	 *            String a ser passada, pode ser null porém sempre retornará null.
	 * @param stringMascara
	 *            Data a ser passada pela máscara em string, pode ser null porém sempre retornará null.
	 * @return <code>a Data através da máscara passada</code>
	 */
	public static String passarDataParaString(Date data, String stringMascara) {
		if (data == null || stringMascara == null)
			return null;

		DateFormat sdf = new SimpleDateFormat(stringMascara);
		return sdf.format(data);
	}

	/**
	 * <p>
	 * Passa uma string para uma data(java.util.Date) de acordo máscara passada por parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.passarStringParaData(null,null)        = null
	 * DateUtils.passarStringParaData(stringData,TipoMascaraData.DD_MM_AAAA)         
	 * DateUtils.passarStringParaData(stringData,TipoMascaraData.DD_MM_AAAA_HH_MM)   
	 * DateUtils.passarStringParaData(stringData,TipoMascaraData.DD_MM_AAAA_HH_MM_SS)
	 * DateUtils.passarStringParaData(stringData,TipoMascaraData.DD_MM_AA)           
	 * DateUtils.passarStringParaData(stringData,TipoMascaraData.DD_MM_AA_HH_MM)     
	 * DateUtils.passarStringParaData(stringData,TipoMascaraData.DD_MM_AA_HH_MM_SS)
	 * </pre>
	 * 
	 * @param value
	 *            String a ser passada, pode ser null porém sempre retornará null.
	 * @param tipoMascaraData
	 *            Data a ser passada pela máscara br.jus.cnj.enums.utils.TipoMascaraData, pode ser null porém sempre retornará null.
	 * @return <code>a Data através da máscara passada</code>
	 * 
	 * @throws ParseException
	 */
	public static Date passarStringParaData(String dataString, TipoMascaraData tipoMascaraData) throws ParseException {
		if (StringUtils.isEmBranco(dataString) || tipoMascaraData == null)
			return null;

		SimpleDateFormat fmt = new SimpleDateFormat(tipoMascaraData.getMascara());
		Date data = fmt.parse(dataString);
		return data;
	}

	/**
	 * <p>
	 * Obtém uma instancia de Calendar com a data do segundo parâmetro setada nessa instância.
	 * </p>
	 * 
	 * 
	 * @param value
	 *            Data a ser setada no Calendar
	 * @return <code>Calendar com a Data setada</code>
	 */
	public static Calendar getInstanciaCalendarComDataSetada(Date data) throws ParseException {
		if (data == null)
			return null;

		Calendar cal = Calendar.getInstance();
		cal.setTime(data);

		return cal;
	}

	/**
	 * <p>
	 * Adiciona ou diminui anos em uma data passada por parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.somarDiminuirAnos(null,0)   = null
	 * DateUtils.somarDiminuirAnos(data,0)   = 31/01/2014 
	 * DateUtils.somarDiminuirAnos(data,2)   = 31/01/2016	
	 * DateUtils.somarDiminuirAnos(data,-3)  = 31/01/2011
	 * </pre>
	 * 
	 * @param data
	 *            Data a ser alterada, pode ser null porém sempre retornará null.
	 * @param qtdAno
	 *            Anos a serem somados/diminuídos, pode ser null porém sempre retornará null.
	 * @return <code>data modificada</code>
	 */
	public static Date somarDiminuirAnos(Date data, int qtdAno) throws ParseException {

		return somarDiminuirDataCalendar(data, qtdAno, Calendar.YEAR);

	}

	/**
	 * <p>
	 * Adiciona ou diminui meses em uma data passada por parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.somarDiminuirMeses(null,0)   = null
	 * DateUtils.somarDiminuirMeses(data,0)   = 31/01/2014 
	 * DateUtils.somarDiminuirMeses(data,2)   = 31/01/2016	
	 * DateUtils.somarDiminuirMeses(data,-3)  = 31/01/2011
	 * </pre>
	 * 
	 * @param value
	 *            data a ser alterada, pode ser null porém sempre retornará null.
	 * @param qtdMeses
	 *            Meses a serem somados/diminuídos, pode ser null porém sempre retornará null.
	 * @return <code>data modificada</code>
	 */
	public static Date somarDiminuirMeses(Date data, int qtdMeses) throws ParseException {

		return somarDiminuirDataCalendar(data, qtdMeses, Calendar.MONTH);

	}

	/**
	 * <p>
	 * Adiciona ou diminui dias em uma data passada por parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.aumentarDiminuirDias(null,0)   = null
	 * DateUtils.aumentarDiminuirDias(data,0)   = 31/01/2014 
	 * DateUtils.aumentarDiminuirDias(data,2)   = 02/02/2014	
	 * DateUtils.aumentarDiminuirDias(data,-3)  = 28/01/2014
	 * </pre>
	 * 
	 * @param value
	 *            data a ser alterada, pode ser null porém sempre retornará null.
	 * @param qtdDias
	 *            Dias a serem somados/diminuídos, pode ser null porém sempre retornará null.
	 * @return <code>data modificada</code>
	 */
	public static Date aumentarDiminuirDias(Date data, int qtdDias) throws ParseException {

		return somarDiminuirDataCalendar(data, qtdDias, Calendar.DAY_OF_MONTH);

	}

	/**
	 * <p>
	 * Método privado que adiciona ou diminui os campos em uma data passada por parâmetro.
	 * </p>
	 * 
	 * <pre>
	 * DateUtils.somarDiminuirDataCalendar(data,8, Calendar.DAY_OF_MONTH)
	 * DateUtils.somarDiminuirDataCalendar(data,0, Calendar.MONTH) 
	 * DateUtils.somarDiminuirDataCalendar(data,2, Calendar.YEAR)
	 * </pre>
	 * 
	 * @param value
	 *            data a ser alterada, pode ser null porém sempre retornará null.
	 * @return <code>data modificada</code>
	 */
	private static Date somarDiminuirDataCalendar(Date data, int qtd, int campo) throws ParseException {
		Calendar cal = getInstanciaCalendarComDataSetada(data);

		cal.add(campo, qtd);

		return cal.getTime();
	}

	/**
	 * <p>
	 * Método que valida se uma data está entre duas datas(inicial e final)
	 * </p>
	 * 
	 * 
	 * @param dataBetween
	 *            data que deverá estar entre uma data e outra, pode ser null porém sempre retornará false.
	 * @param dataInicial
	 *            data inicial para a verificação, pode ser null porém sempre retornará false.
	 * @param dataFinal
	 *            data final para a verificação, pode ser null porém sempre retornará false.
	 * @param isDesconsiderarHoras
	 *            boolean para desconsiderar as horas das datas na verificação
	 * @return <code>true</code> se a data estiver entre a data inicial e a data final
	 */
	public static boolean validarDataBetweenDatas(Date _dataBetween, Date _dataInicial, Date _dataFinal, boolean isDesconsiderarHoras) {
		Date dataBetween = (Date) _dataBetween.clone();
		Date dataInicio = (Date) _dataInicial.clone();
		Date dataFinal = (Date) _dataFinal.clone();

		if (isDesconsiderarHoras) {
			prepararDatasParaValidacaoBetweenDesconsiderandoHoras(dataBetween, dataInicio, dataFinal);
		}

		Date dataFim = new Date(dataFinal.getTime());
		if ((dataBetween.compareTo(dataInicio) >= 0) && (dataFim.compareTo(dataBetween) >= 0)) {
			return true;
		}

		return false;
	}

	private static void prepararDatasParaValidacaoBetweenDesconsiderandoHoras(Date dataBetween, Date dataInicio, Date dataFinal) {
		zerarHorasData(dataBetween);
		zerarHorasData(dataInicio);
		zerarHorasData(dataFinal);
	}

	@SuppressWarnings("deprecation")
	private static void zerarHorasData(Date data) {
		data.setHours(0);
		data.setMinutes(0);
		data.setSeconds(0);
	}

	/**
	 * <p>
	 * Retorna  quantidade de dias a partir de uma data informada comparando com a data atual.
	 * </p>
	 * 
	 * <pre>
	 * </pre>
	 * 
	 * @param date
	 *            Data a qual deseja comparar com a data atual.
	 * @return <code>quantidade de dias</code>
	 */
	public static int quantidadeDias(Date date) {
		Calendar calendarHoje = Calendar.getInstance();
		Calendar calendarDataParamentro = Calendar.getInstance();

		DateTime dataHoje = new DateTime(calendarHoje.getTime());
		calendarDataParamentro.setTime(date);

		DateTime dataParametro = new DateTime(calendarDataParamentro.getTime());

		return Days.daysBetween(dataParametro, dataHoje).getDays();
	}

}
